#include "Thermometer.h"

#include <math.h>

Thermometer::Thermometer(const char *name) : name(name), lastTemperature(0.0), lastUpdateTime(0) {
}

Thermometer::~Thermometer() {
}

const time_t Thermometer::getLastUpdate() const {
    return lastUpdateTime;
}

const temperature_t Thermometer::getValue() const {
    return lastTemperature;
}

const bool Thermometer::isSupported(String const &request) {
    return request.startsWith(name);
}

const error_t Thermometer::processCommand(String const &request, String &response) {
    if (request.equals(name + "+CURRENT?")) {
        temperature_t dbl = getValue();
        int precision = 2;
        int whole = (int) dbl;
        int fractional = (int) ((dbl - floor(dbl)) * pow(10, precision));

        response = response + whole;
        response = response + ".";
        response = response + fractional;
        response = response + "";
        response = response + "+";
        response = response + *Timer::toString(lastUpdateTime, DATETIME);
    }
    return NO_ERROR;
}

DallasThermometer::DallasThermometer(const char *name, uint8_t pin, DeviceAddress address)
        : Thermometer(name), requestTime(0), responseTimeout(1000) {
    wire = new OneWire(pin);
    sensor = new DallasTemperature(wire);

    for (int i = 0; i < 8; ++i) {
        this->address[i] = address[i];
    }
}

DallasThermometer::~DallasThermometer() {
    delete sensor;
    delete wire;
}

const error_t DallasThermometer::begin() {
    sensor->begin();

    if (!sensor->validAddress(address)) {
        return ERROR_THERMOMETER_ADDRESS;
    }

    setResolution(THERMOMETER_RESOLUTION);
    sensor->setWaitForConversion(false);

    return NO_ERROR;
}

const error_t DallasThermometer::tick(const tick_t ticks, const time_t time) {
    if (millis() - requestTime >= responseTimeout) {
        float temp = sensor->getTempC(address);

        if (temp != DEVICE_DISCONNECTED) { // don't update if value is not correct
            lastUpdateTime = time;
            lastTemperature = temp;
        }
        sendRequest();
    }
    return NO_ERROR;
}

const uint8_t DallasThermometer::getResolution() {
    return sensor->getResolution(address);
}

void DallasThermometer::setResolution(const uint8_t resolution) {
    sensor->setResolution(address, resolution);
    responseTimeout = (800 / (1 << (12 - resolution)));
}

const error_t DallasThermometer::processCommand(String const &request, String &response) {
    response = "";
    if (request.equals(name + "+RESOLUTION?")) {
        response = response + getResolution();
    } else if (request.startsWith(name + "+RESOLUTION=")) {
        setResolution((uint8_t const) request.substring(16).toInt());
        response = response + getResolution();
    }
    return Thermometer::processCommand(request, response);
}

void DallasThermometer::sendRequest() {
    sensor->requestTemperaturesByAddress(address);
    requestTime = millis();
}